﻿using LJ.Abstractions.Extensions;
using LJ.Abstractions.Reflection;
using LJ.WebApi.Abstractions;
using LJ.WebApi.Abstractions.Attributes;
using System.Collections.Concurrent;
using System.Reflection;

namespace LJ.WebApi.Mvc.Internal;


public class WebApiHandlerManager : IWebApiHandlerManager
{
    private readonly ConcurrentDictionary<Type, WebApiHandlerDescriptor> _webApiHandlerDescriptors = [];

    private readonly IReflectionManager _reflectionManager;

    public WebApiHandlerManager(IReflectionManager reflectionManager)
    {
        _reflectionManager = reflectionManager;

        var types = FindTypes();

        foreach (var type in types)
        {
            var webApiHandlerAttribute = type.GetCustomAttribute<WebApiHandlerAttribute>();
            if (webApiHandlerAttribute != null)
            {
                var routeType = GetRouteType(type);
                var requestType = GetRequestType(type);
                var responseType = GetResponseType(type);
                _webApiHandlerDescriptors.AddOrUpdate(type,
                    a => new WebApiHandlerDescriptor(webApiHandlerAttribute.Controller.ToLower(), webApiHandlerAttribute.Action.ToLower(),
                    webApiHandlerAttribute.Type, routeType, requestType, responseType, type),

                    (a, b) => new WebApiHandlerDescriptor(webApiHandlerAttribute.Controller.ToLower(), webApiHandlerAttribute.Action.ToLower(),
                    webApiHandlerAttribute.Type, routeType, requestType, responseType, type));
            }
        }

    }
    public bool IsApiController(Type type)
    {
        return _webApiHandlerDescriptors.ContainsKey(type);
    }

    public WebApiHandlerDescriptor GetWebApiHandlerDescriptor(Type type)
    {
        if (_webApiHandlerDescriptors.TryGetValue(type, out var handlerDescriptor))
        {
            return handlerDescriptor;
        }
        throw new ArgumentException($"[WebApiHandlerDescriptor] not : {type.FullName}");
    }

    private List<Type> FindTypes()
    {
        Type[] baseTypes = [typeof(IWebApi)];
        return _reflectionManager.GetTypes(type => (baseTypes.Any(b => type.IsDeriveClassFrom(b))) && type.HasAttribute<WebApiHandlerAttribute>());
    }


    private static Type? GetRequestType(Type type)
    {

        Type[] baseType1s = [typeof(IWebApiRequestHandler<>), typeof(IWebApiRequestResponseHandler<,>)];
        var handlerType = type.GetTypeInfo()
            .ImplementedInterfaces
            .SingleOrDefault(r => r.IsGenericType && (baseType1s.Contains(r.GetGenericTypeDefinition())));
        if (handlerType!=null)
        {
            return handlerType.GetGenericArguments().FirstOrDefault();
        }
        
        Type[] baseType2s = [typeof(IWebApiRouteRequestHandler<,>),typeof(IWebApiRouteRequestResponseHandler<,,>)];
        handlerType = type.GetTypeInfo()
            .ImplementedInterfaces
            .SingleOrDefault(r => r.IsGenericType && (baseType2s.Contains(r.GetGenericTypeDefinition())));
        if (handlerType != null)
        {
            return handlerType.GetGenericArguments().Skip(1).FirstOrDefault();
        }

        return default;

    }

    private static Type? GetResponseType(Type type)
    {
        Type[] baseTypes = [typeof(IWebApiResponseHandler<>), typeof(IWebApiRouteResponseHandler<,>), typeof(IWebApiRequestResponseHandler<,>), typeof(IWebApiRouteRequestResponseHandler<,,>)];
        return type.GetTypeInfo()
            .ImplementedInterfaces
            .SingleOrDefault(r => r.IsGenericType && (baseTypes.Contains(r.GetGenericTypeDefinition())))?
            .GetGenericArguments()
            .LastOrDefault();
    }

    private static Type? GetRouteType(Type type)
    {
        Type[] baseTypes = [typeof(IWebApiRouteHandler<>), typeof(IWebApiRouteRequestHandler<,>),typeof(IWebApiRouteResponseHandler<,>),typeof(IWebApiRouteRequestResponseHandler<,,>)];
        return type.GetTypeInfo()
            .ImplementedInterfaces
            .SingleOrDefault(r => r.IsGenericType && (baseTypes.Contains(r.GetGenericTypeDefinition())))?
            .GetGenericArguments()
            .Take(1)
            .FirstOrDefault();
    }

}
